# coding=utf8
__author__ = 'lijian'
from utils import byteUtil
import decode
import struct
import time

from utils import crc16
from utils import byteUtil

_powDict = {0: 0,
            1: 1,
            2: 2,
            4: 3,
            8: 4,
            16: 5,
            32: 6,
            64: 7,
            128: 8}


class Frame(object):
    def __init__(self, dataRegion=None, address=None):
        self._dataRegion = dataRegion
        self._address = address
        self._len = 0

        self.raw = None

    def encode(self):
        self.raw = []

        data = self.dataRegion.encode()
        self._len = len(data) + 8

        self.raw.append(0x68)
        self.raw.extend(byteUtil.encodeShort(self._len))
        self.raw.extend(byteUtil.encodeShort(self._len))
        self.raw.append(0x68)
        self.raw.extend(self.address)
        self.raw.extend(data)
        self.raw.extend(self._CRC(self.raw[6:]))
        self.raw.append(0x16)

        return self.raw

    def _CRC(self, data):
        value = crc16.CRC16(data)
        raw = byteUtil.encodeShortBigEndian(value)
        return raw

    @property
    def dataRegion(self):
        return self._dataRegion

    @property
    def address(self):
        return self._address

    @property
    def len(self):
        return self._len

    def pack(self):
        if not self.raw:
            self.encode()
        return struct.pack('B' * len(self.raw), *self.raw)

    def info(self):
        return self.dataRegion.info()


class AFN(object):
    def __init__(self, code=None, dir=None, prm=None, acd=None, cmd=None):
        if code is not None:
            self._code = code
        else:
            self._code = 0
            self._code = cmd | self._code
            self._code = (acd << 5) | self._code
            self._code = (prm << 6) | self._code
            self._code = (dir << 7) | self._code

    @property
    def PRM(self):
        return self._code >> 7 & 1

    @property
    def DIR(self):
        return self._code >> 6 & 1

    @property
    def ACD(self):
        return self._code >> 5 & 1

    @property
    def CMD(self):
        return self._code & 0x1F

    @property
    def code(self):
        return self._code

    def info(self):
        return "prm:%d,dir:%d,acd:%d,cmd:%d" % (self.PRM, self.DIR, self.ACD, self.CMD)


class SEQ(object):
    def __init__(self, code=None, tpv=None, fir=None, fin=None, con=None, seq=None):
        if code:
            self._code = code
        else:
            self._code = 0
            self._code = seq
            self._code = (con << 4) | self._code
            self._code = (fin << 5) | self._code
            self._code = (fir << 6) | self._code
            self._code = (tpv << 7) | self._code

    @property
    def TpV(self):
        return self._code >> 7 & 1

    @property
    def FIR(self):
        return self._code >> 6 & 1

    @property
    def FIN(self):
        return self._code >> 5 & 1

    @property
    def CON(self):
        return self._code >> 4 & 1

    @property
    def Seq(self):
        return self._code & 0xF

    @property
    def code(self):
        return self._code

    def info(self):
        return "tpv:%d,fir:%d,fin:%d,con:%d,seq:%d" % (self.TpV, self.FIR, self.FIN, self.CON, self.Seq)


class AUX(object):
    def __init__(self, counter=None, tp=None):
        self.counter = counter
        self.tp = tp


    def encode(self):
        self.raw = []

        if self.counter:
            self.raw.append(self.counter.EC1)
            self.raw.append(self.counter.EC2)

        if self.tp:
            self.raw.append(self.tp.pfc)
            self.raw.extend(self.tp.time)
            self.raw.append(self.tp.delayAllow)
        return self.raw

    def info(self):
        # return "EC1:%d,EC2:%d,PFC:%d time:(%d %d:%d:%d),delayAllow:%d" \
        # % (
        # self._EC1, self._EC2, self._pfc, self._time[0], self._time[1], self._time[2], self._time[3],
        #     self._delayAllow)
        return "counter:%r,tp:%r" % (self.counter, self.tp)


class EventCounter(object):
    def __init__(self, ec1, ec2):
        self._EC1 = ec1
        self._EC2 = ec2

    @property
    def EC1(self):
        return self._EC1

    @property
    def EC2(self):
        return self.EC2


class TP(object):
    def __init__(self, pfc, delayAllow, timestamp=None):
        self.pfc = pfc
        self.delayAllow = delayAllow

        if timestamp is not None:
            self.time = timestamp
        else:
            tm = time.localtime(time.time())
            self.time = [tm.tm_sec, tm.tm_min, tm.tm_hour, tm.tm_mday]


class DataUnit(object):
    def __init__(self, pn, fn, data=None):

        if isinstance(pn, list) and isinstance(fn, list):
            self._da = pn
            self._dt = fn

            d = self._da[1]
            if self._da[1] > 0:
                d = self._da[1] - 1

            self._pn = d * 8 + _powDict[self._da[0]]

            self._fn = self._dt[1] * 8 + _powDict[self._dt[0]]
        else:
            self._da = [0, 0]
            self._dt = [0, 0]

            self._pn = pn
            self._fn = fn

            if pn != 0:
                self._da[0] = 1 << ( ( pn - 1 ) % 8)
                self._da[1] = ( (pn - 1) / 8) + 1
            if fn != 0:
                self._dt[0] = 1 << ( (fn - 1) % 8)
                self._dt[1] = fn / 8

        self._data = data

    @property
    def rawData(self):
        return self._data

    @rawData.setter
    def rawData(self, data):
        self._data = data

    @property
    def DA(self):
        return self._da

    @property
    def DT(self):
        return self._dt

    @property
    def PN(self):
        return self._pn

    @property
    def FN(self):
        return self._fn

    def info(self):
        return "FN:%d,pn:%d" % (self.FN, self.PN)

    def encode(self):
        self.raw = []
        self.raw.extend(self._da)
        self.raw.extend(self._dt)
        if self._data:
            self.raw.extend(self._data)
        return self.raw


class FrameDataRegion(object):
    def __init__(self, afn, seq, dataUnit=None, aux=None):
        self._dataUnits = []
        self._afn = afn
        self._seq = seq

        if dataUnit:
            if isinstance(dataUnit, list):
                self._dataUnits.extend(dataUnit)
            else:
                self._dataUnits.append(dataUnit)

        self._aux = aux

    def addDataUnit(self, unit):
        self._dataUnits.append(unit)

    @property
    def AFN(self):
        return self._afn

    @property
    def AUX(self):
        return self._aux

    @AUX.setter
    def AUX(self, value):
        self._aux = value

    @property
    def SEQ(self):
        return self._seq

    @property
    def dataUnits(self):
        return self._dataUnits

    def encode(self):
        self.raw = []
        self.raw.append(self._afn.code)
        self.raw.append(self._seq.code)
        for u in self._dataUnits:
            if u:
                self.raw.extend(u.encode())
        if self._aux:
            self.raw.extend(self._aux.encode())
        return self.raw

    def info(self):

        if not self._aux:
            auxInfo = None
        else:
            auxInfo = self._aux.info()

        info = "AFN:(%s),SEQ:(%s),AUX:%s, unitData size:%d " \
               % (self._afn.info(), self._seq.info(), auxInfo, len(self._dataUnits))
        for dataUnit in self._dataUnits:
            info = info + "[" + dataUnit.info() + "]"

        return info




